/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.yonsei.iwb.smarttv.index;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import edu.yonsei.iwb.smarttv.jena.ext.JenaModelType;
import edu.yonsei.iwb.smarttv.jena.ext.JenaResourceType;
import edu.yonsei.iwb.smarttv.model.SMARTTVModel;
import edu.yonsei.iwb.smarttv.query.TermAnalyzer;
import edu.yonsei.iwb.smarttv.util.SMARTTVLog;
import edu.yonsei.iwb.smarttv.util.UtilMethodSet;
import java.io.File;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;

/**
 *
 * @author Onlyjiny
 */
public class OntologyIndexer {

    private Directory index;
    private TermAnalyzer ta;
    private SMARTTVModel tvModel;

    public OntologyIndexer() {
        File file = new File(IndexType.INDEX_DIR);
        try {
            this.index = FSDirectory.open(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public OntologyIndexer(int type, String target) {
        tvModel = new SMARTTVModel(JenaModelType.MEMORY_MODEL);
        new OntologyIndexer(type, tvModel);
    }

    public OntologyIndexer(int type, SMARTTVModel tvModel) {
        this.tvModel = tvModel;
        if (type == IndexType.FILE_INDEX) {
            File file = new File(IndexType.INDEX_DIR);
            try {
                this.index = FSDirectory.open(file);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            this.index = new RAMDirectory();
        }
        this.ta = TermAnalyzer.getTermAnalyzer();
        this.makeIndex(this.tvModel);
    }

    private void makeIndex(SMARTTVModel tvModel) {
        try {
            // rdfs:label과 skos:prefLabel에 대한 인덱스 생성
            Model model = tvModel.getJenaModel();
            StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_34);
            IndexWriter w = new IndexWriter(this.index, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);
            MainIndexedProperties mainIndexProperties = new MainIndexedProperties();
            ResIterator subjects = model.listSubjects();
            while (subjects.hasNext()) {
                Resource resource = subjects.nextResource();
                if (!resource.isURIResource()) {
                    continue;
                }
                Document doc = new Document();
                Field uriField = new Field("uri", resource.getURI(), Field.Store.YES, Field.Index.NOT_ANALYZED);
                Field typeField;
                switch (this.tvModel.getResourceType(resource)) {
                    case JenaResourceType.CLASS:
                        typeField = new Field("type", "Class", Field.Store.YES, Field.Index.NOT_ANALYZED);
                        break;
                    case JenaResourceType.PROPERTY:
                        typeField = new Field("type", "Property", Field.Store.YES, Field.Index.NOT_ANALYZED);
                        break;
                    case JenaResourceType.INSTANCE:
                        typeField = new Field("type", "Instance", Field.Store.YES, Field.Index.NOT_ANALYZED);
                        break;
                    default:
                        typeField = new Field("type", "Undeclared", Field.Store.YES, Field.Index.NOT_ANALYZED);
                }
                doc.add(uriField);
                doc.add(typeField);
                Enumeration<String> keys = mainIndexProperties.keys();
                while (keys.hasMoreElements()) {
                    String fieldName = keys.nextElement();
                    String indexValues = new String();
                    Vector<Resource> propertySet = mainIndexProperties.get(fieldName);
                    for (int i = 0; i < propertySet.size(); i++) {
                        Property property = model.createProperty(propertySet.get(i).getURI());
                        StmtIterator allStatement = model.listStatements(resource, property, (RDFNode) null);
                        while (allStatement.hasNext()) {
                            Statement stmt = allStatement.nextStatement();
                            if (stmt.getObject().isLiteral()) {
                                String literalValue = stmt.getString();
                                Vector<String> terms = ta.getIndexTerms(literalValue);
                                indexValues += UtilMethodSet.getStringForIndex(terms) + " ";
                            }
                        }
                    }
                    Field field;
                    if (!indexValues.isEmpty()) {
                        field = new Field(fieldName, indexValues.trim(), Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS);
                        SMARTTVLog.log(SMARTTVLog.DETAIL, "Indexed resource URI: " + resource.getURI() + ", " + fieldName + " of Indexed Value of resource: " + indexValues + "\n");
                        doc.add(field);
                    }
                }
                w.addDocument(doc);
            }

            // DatatypeProperty에 대한 인덱스 생성
            String genID = "genID_";
            int index_id = 1;
            // range가 다르지만 같은 값을 가지고 있는 것에 대한 고려가 안되어 있음
            Vector<String> checkList = new Vector<String>();
            IndexedDatatypeProperties idp = new IndexedDatatypeProperties(tvModel);
            String fieldName = idp.LABEL4DATATYPEPROPERTY;
            Enumeration<Resource> keys = idp.keys();
            while (keys.hasMoreElements()) {
                Resource r = keys.nextElement();
                String range = idp.get(r);
                NodeIterator ni = model.listObjectsOfProperty(model.createProperty(r.getURI()));
                while (ni.hasNext()) {
                    RDFNode node = ni.next();
                    if (node.isLiteral()) {
                        String literalValue = node.toString();
                        String datatypeValue = UtilMethodSet.getStringForIndex(ta.getIndexTerms(literalValue.substring(0, literalValue.lastIndexOf("^^"))));
                        if (!datatypeValue.isEmpty() && !checkList.contains(datatypeValue)) {
                            String literalValueURI = genID + index_id++;
                            checkList.add(datatypeValue);
                            Document literalDoc = new Document();
                            Field literalUriField = new Field("uri", literalValueURI, Field.Store.YES, Field.Index.NOT_ANALYZED);
                            Field litaralLabelField = new Field(fieldName, datatypeValue, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS);
                            Field literalTypeField = new Field("type", range, Field.Store.YES, Field.Index.NOT_ANALYZED);
                            literalDoc.add(litaralLabelField);
                            literalDoc.add(literalUriField);
                            literalDoc.add(literalTypeField);
                            SMARTTVLog.log(SMARTTVLog.DETAIL, "Indexed literal URI: " + literalValueURI + ", " + "label of Indexed Value of literal: " + datatypeValue + "\n");
                            w.addDocument(literalDoc);
                        }
                    } else {
                        continue;
                    }
                }
            }
            w.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        SMARTTVLog.log(SMARTTVLog.DEFAULT, "::::: Completed index\n");
    }

    public Directory getIndex() {
        return this.index;
    }

}
